package com.bert.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.bert.entity.SkyWalking.*;
import com.bert.entity.SkyWalking.TraceQuery.ConditionEntity;
import com.bert.entity.SkyWalking.TraceQuery.PagingEntity;
import com.bert.entity.SkyWalking.TraceQuery.QueryDurationEntity;
import com.bert.entity.SkyWalking.TraceQuery.TraceQueryEntity;
import com.bert.feign.SkyWalkingClient;
import com.bert.service.SkyWalkingService;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Objects;

@Slf4j
@Service
public class SkyWalkingServiceImpl implements SkyWalkingService {

    @Resource
    private SkyWalkingClient skyWalkingClient;

    @Override
    public List<ServiceResultEntity> getServiceList() {
        String result = skyWalkingClient.getSkyWalking(
                SkyWalkingQueryEntity.builder()
                        .query(SkyWalkingConstant.QUERY_SERVICE)
                        .variables(new ServiceQueryEntity("GENERAL"))
                        .build());
        return this.jsonParse(result, ServiceResultEntity.class, "data", "services");
    }

    @Override
    public List<TraceResultEntity> getTraceList(String serviceId, Long timeOut, String startTime, String endTIme, Integer pageNum, Integer pageSize) {
        String result = skyWalkingClient.getSkyWalking(
                SkyWalkingQueryEntity.builder()
                        .query(SkyWalkingConstant.QUERY_TRACE)
                        .variables(this.getTraceQueryEntity(serviceId, timeOut, startTime, endTIme, pageNum, pageSize))
                        .build());
        return this.jsonParse(result, TraceResultEntity.class, "data", "data", "traces");
    }

    private TraceQueryEntity getTraceQueryEntity(String serviceId, Long timeOut, String startTime, String endTIme, Integer pageNum, Integer pageSize){
        ConditionEntity condition = ConditionEntity.builder()
                .serviceId(serviceId)
                .maxTraceDuration(null)
                .minTraceDuration(timeOut)
                .paging(new PagingEntity(pageNum, pageSize))
                .queryDuration(new QueryDurationEntity(startTime, endTIme, "MINUTE"))
                .queryOrder("BY_START_TIME")
                .traceState("ALL")
                .build();
        return new TraceQueryEntity(condition);
    }

    @Override
    public List<SpanResultEntity> getSpanList(String trace) {
        String result = skyWalkingClient.getSkyWalking(
                SkyWalkingQueryEntity.builder()
                        .query(SkyWalkingConstant.QUERY_SPAN)
                        .variables(new SpanQueryEntity(trace))
                        .build());
        return this.jsonParse(result, SpanResultEntity.class, "data", "trace", "spans");
    }

    @Override
    public List<TagInfoEntity> getTagInfoList(String startTime, String endTime, Long timeOut, Integer pageNum, Integer pageSize) {
        List<TagInfoEntity> tagInfoList = new ArrayList<>();
        List<ServiceResultEntity> serviceList = this.getServiceList();

        /*
          1. 遍历服务，获取服务的traceList.
          2. 遍历traceList，获取超时的traces
          3. 遍历traces, 获取超时的spanList
          4. 遍历spanList, 获取tagInfoList信息并返回
         */
        serviceList.forEach(service -> {
            List<TraceResultEntity> traceList = this.getTraceList(service.getId(), timeOut, startTime, endTime, pageNum, pageSize);
            List<String> traces = traceList.stream().map(TraceResultEntity::getTraceIds).flatMap(Collection::stream).toList();
            traces.forEach(trace->{
                List<SpanResultEntity> spanList = this.getSpanList(trace);
                List<SpanResultEntity> timeOutList = spanList.stream().filter(f -> (f.getEndTime() - f.getStartTime()) > timeOut).toList();
                TagInfoEntity tagInfo = new TagInfoEntity();
                List<TagInfoEntity> childTags = new ArrayList<>();
                for (SpanResultEntity e : timeOutList) {
                    if (e.getRefs().length > 0) {
                        childTags.add(this.getTagInfo(e.getTags(), e));
                    } else {
                        tagInfo = this.getTagInfo(e.getTags(), e);
                    }
                }
                tagInfo.setChild(childTags);
                tagInfoList.add(tagInfo);
            });
        });

        return tagInfoList;
    }

    private TagInfoEntity getTagInfo(List<TagEntity> tagList, SpanResultEntity spanEntity){
        TagInfoEntity tagInfo = new TagInfoEntity();
        tagList.forEach(tag -> {
            String key = tag.getKey();
            if (key.equals("http.method")) {
                tagInfo.setHttpMethod(tag.getValue());
            }
            if (key.equals("url")) {
                tagInfo.setUrl(tag.getValue());
                URL url = this.getURL(tag.getValue());
                tagInfo.setIp(url.getHost());
                tagInfo.setPort(url.getPort());
            }
            if (key.equals("http.status_code")) {
                tagInfo.setHttpStatusCode(tag.getValue());
            }
        });
        tagInfo.setService(spanEntity.getServiceCode());
        tagInfo.setEndpointName(spanEntity.getEndpointName());
        tagInfo.setComponent(spanEntity.getComponent());
        tagInfo.setTraceId(spanEntity.getTraceId());
        tagInfo.setRefs(spanEntity.getRefs());
        tagInfo.setTime(spanEntity.getEndTime() - spanEntity.getStartTime());
        return tagInfo;
    }

    private URL getURL(String url){
        try {
            if (!url.startsWith("http")) {
                url = "http" + url.substring(url.indexOf(":"));
            }
            return new URL(url);
        } catch (MalformedURLException e) {
            throw new RuntimeException(e);
        }
    }

//    private <T> List<T> jsonParse(String source, Class<T> clazz, String... args){
//        JsonNode json = JsonUtil.parseJSONObject(source);
//        String error = JsonUtil.getString(json, "errors");
//        if (Objects.nonNull(error)) {
//            List<ErrorEntity> entities = JsonUtil.ofList(error, ErrorEntity.class);
//            log.error(clazz.getName() + "获取数据异常：" + error);
//            throw new RuntimeException("获取数据异常");
//        }
//
//        String temp = "";
//        for (int i = 0; i < args.length; i++) {
//            if (i > 0) {
//                json = JsonUtil.parseJSONObject(temp);
//            }
//            temp = JsonUtil.getString(json, args[i]);
//        }
//        return JsonUtil.ofList(temp, clazz);
//    }

    private <T> List<T> jsonParse(String source, Class<T> clazz, String... args){
        JSONObject json = JSON.parseObject(source);
        String error = json.getString("errors");
        if (Objects.nonNull(error)) {
            List<ErrorEntity> entities = JSON.parseArray(error, ErrorEntity.class);
            log.error(clazz.getName() + "获取数据异常：" + error);
            throw new RuntimeException("获取数据异常");
        }

        String temp = "";
        for (int i = 0; i < args.length; i++) {
            if (i > 0) {
                json = JSON.parseObject(temp);
            }
            String arg = args[i];
            temp = json.getString(arg);
        }
        return JSON.parseArray(temp, clazz);
    }

}
